package com.ship.dispatch.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ship.common.core.base.Page;
import com.ship.common.core.constant.ShipApiConstants;
import com.ship.common.core.domain.SpPortData;
import com.ship.common.core.exception.ServiceException;
import com.ship.common.core.utils.*;
import com.ship.common.core.web.domain.BaseResultCode;
import com.ship.common.core.web.domain.JsonResult;
import com.ship.common.business.service.impl.BaseServiceImpl;
import com.ship.common.security.utils.SecurityUtils;
import com.ship.dispatch.bean.*;
import com.ship.dispatch.bo.HireTimeDetailsBO;
import com.ship.dispatch.bpm.service.ProcessInstanceService;
import com.ship.dispatch.bpm.vo.BpmStartVO;
import com.ship.dispatch.mapper.ConShippingMapper;
import com.ship.dispatch.mapper.SpArrangeSchemeMapper;
import com.ship.dispatch.mapper.SpPortInformationMapper;
import com.ship.dispatch.model.SpDispatchSchemeDetailModel;
import com.ship.dispatch.model.SpYearPlanSchemeModel;
import com.ship.dispatch.model.Vo.*;
import com.ship.dispatch.model.dto.ArrangeSchemeChartSimpleDto;
import com.ship.dispatch.model.dto.SchemeChartSimpleDto;
import com.ship.dispatch.model.dto.SpArrangeSchemeDetailStatusDto;
import com.ship.dispatch.service.*;
import com.ship.dispatch.util.CommonUtil;
import com.ship.dispatch.util.IDUtil;
import com.ship.dispatch.util.NumberFormatUtil;
import com.ship.system.api.BpmInstanceService;
import com.ship.system.api.model.ProcessInstanceParam;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author zhangyh
* @description 针对表【sp_arrange(货船排布)】的数据库操作Service实现
* @createDate 2023-11-03 10:17:22
*/
@Service
public class SpArrangeSchemeServiceImpl extends BaseServiceImpl<SpArrangeSchemeMapper, SpArrangeScheme>
    implements SpArrangeSchemeService {

    @Autowired
    private SpArrangeSchemeDetailService spArrangeSchemeDetailService;

    @Autowired
    private SpDispatchSchemeDetailService spDispatchSchemeDetailService;

    @Autowired
    private ConShippingService conShippingService;

    @Autowired(required = false)
    private SpPortInformationMapper spPortInformationMapper;

    @Autowired
    @Lazy
    private SpInformationService spInformationService;

    @Autowired
    @Lazy
    private SpDispatchSchemeExecuteService spDispatchSchemeExecuteService;

    @Autowired
    private ProcessInstanceService processInstanceService;

    @Resource
    private BpmInstanceService bpmInstanceService;

    @Autowired
    private SpPortRouteDistanceService spPortRouteDistanceService;

    private static String shipServerHost;


    private static String shipServerAppKey;

    @Value("${ship_server.host}")
    public void setShipServerHost(String shipServerHost) {
        this.shipServerHost = shipServerHost;
    }

    @Value("${ship_server.app_key}")
    public void setShipServerAppKey(String shipServerAppKey) {
        this.shipServerAppKey = shipServerAppKey;
    }

    @Override
    public void getArrangeSchemeChart(Long arrangeSchemeDetailId, int executeStatus) {
        spArrangeSchemeDetailService.update(
                new LambdaUpdateWrapper<SpArrangeSchemeDetail>()
                .set(SpArrangeSchemeDetail::getExecuteStatus,executeStatus)
                .eq(SpArrangeSchemeDetail::getId,arrangeSchemeDetailId)
        );
        spDispatchSchemeDetailService.update(
                new LambdaUpdateWrapper<SpDispatchSchemeDetail>()
                        .set(SpDispatchSchemeDetail::getExecuteStatus,executeStatus)
                        .eq(SpDispatchSchemeDetail::getArrangeSchemeDetailId,arrangeSchemeDetailId)
        );
    }

    @Override
    public JsonResult getArrangeSchemeChart(SchemeChartSimpleDto dto) {
        //获取所有船货排布方案详情
        List<SpArrangeSchemeDetail> details = spArrangeSchemeDetailService.getBaseMapper().selectList(
                new LambdaQueryWrapper<>()
        );
        if(details.size() == 0){
            return JsonResult.success();
        }

        JSONObject resultData = new JSONObject();

        //未下发的数据列表
        List<SpArrangeSchemeDetail> unSendList = details.stream()
                .filter(detail -> detail.getExecuteStatus() == 1 || detail.getExecuteStatus() == 2).collect(Collectors.toList());

        // 获取甘特图时间轴的开始时间（未下发的第一船货的装载时间）
        if(unSendList.size() > 0){
            SpArrangeSchemeDetail detailFirst = unSendList.stream().min(Comparator.comparing((SpArrangeSchemeDetail::getExpectedLoadingDate))).get();
            resultData.put("firstShipLoadingTime", cn.hutool.core.date.DateUtil.offsetMonth(detailFirst.getExpectedLoadingDate(),-2).getTime());
        }else{
            //全部都下发了则以当前时间
            resultData.put("firstShipLoadingTime",new Date().getTime());
        }

        List<SpInformation> spInformations = spInformationService.getAllSpInformationUsed(null);
        Map<String,List<SpInformation>> shipMap = new HashMap<>();
        if(spInformations.size() > 0){
            shipMap = spInformations.stream().collect(Collectors.groupingBy(SpInformation::getNameOfVessel));
        }

        //船舶排布
        Map<String,List<SpArrangeSchemeDetail>> boatMap = details.stream().collect(Collectors.groupingBy(SpArrangeSchemeDetail::getShipName));
        JSONArray boatsArray = new JSONArray();
        for (SpInformation spInformation : spInformations) {
            String boatName = spInformation.getNameOfVessel();
            JSONObject boatDetailJson = new JSONObject();
            List<SpArrangeSchemeDetail> boatDetails = boatMap.get(boatName) != null ? boatMap.get(boatName) : new LinkedList<>();
            if(boatDetails.size() == 0){
                continue;
            }
            boatDetailJson.put("imo",spInformation.getImo());
            boatDetailJson.put("storageCapacity",spInformation.getStorageCapacity());
            boatDetailJson.put("storageCapacityMmbtu",spInformation.getStorageCapacityMmbtu());
            boatDetailJson.put("shipName",boatName);
            boatDetailJson.put("details",boatDetails);
            boatsArray.add(boatDetailJson);
        }
        resultData.put("shipNames",boatsArray);

        //资源排布
        Map<String,List<SpArrangeSchemeDetail>> goodsMap = details.stream().collect(Collectors.groupingBy(SpArrangeSchemeDetail::getGoodsNum));
        List<JSONObject> goodsArray = new LinkedList<>();
        for (String goodsName : goodsMap.keySet()) {
            JSONObject goodsDetailJson = new JSONObject();
            goodsDetailJson.put("goodsName",goodsName);
            goodsDetailJson.put("details",goodsMap.get(goodsName));
            goodsDetailJson.put("number",goodsMap.get(goodsName).get(0).getSchemeNumber());
            goodsArray.add(goodsDetailJson);
        }
        goodsArray = goodsArray.stream().sorted(
                Comparator.comparingInt(a -> ((JSONObject)a).getIntValue("number"))).collect(Collectors.toList());
        resultData.put("goodsNames",goodsArray);
        //资源方排布
        Map<String,List<SpArrangeSchemeDetail>> sourceSideMap = details.stream().collect(Collectors.groupingBy(SpArrangeSchemeDetail::getSeller));
        JSONArray sourceSideArray = new JSONArray();
        for (String sourceSideName : sourceSideMap.keySet()) {
            JSONObject sourceSideDetailJson = new JSONObject();
            sourceSideDetailJson.put("sellerName",sourceSideName);
            sourceSideDetailJson.put("details",sourceSideMap.get(sourceSideName));
            sourceSideArray.add(sourceSideDetailJson);
        }
        resultData.put("sellerNames",sourceSideArray);
        return JsonResult.success(resultData);
    }

    @Override
    public JsonResult getSchemeDetailPage(SpArrangeSchemeDetail dto) {
        if(StringUtils.isBlank(dto.getContractName())){
            dto.setContractName(null);
        }
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("voyage_no");
        Page<SpArrangeSchemeDetail> resultPage = spArrangeSchemeDetailService.page(dto,queryWrapper);
        for (SpArrangeSchemeDetail detail : resultPage.getList()) {
            if(detail.getIsFobSell() != null && detail.getIsFobSell().intValue() == 1){
                detail.setImo("");
                detail.setShipName("");
            }
            if(detail.getLoadingCapacity() != null){
                detail.setLoadingCapacityString(NumberFormatUtil.numericFormatting(detail.getLoadingCapacity())+" m³");
            }
            if(detail.getSingleGoodsQuantity() != null){
                detail.setSingleGoodsQuantityString(NumberFormatUtil.numericFormatting(detail.getSingleGoodsQuantity())+" MMBtu");
            }
            if(detail.getOilConsumption() != null){
                detail.setOilConsumptionString(NumberFormatUtil.numericFormatting(detail.getOilConsumption()));
            }
            if(detail.getExpectedLoadingPortId() != null){
                List<String> portIds = new LinkedList<>();
                portIds.add(String.valueOf(detail.getExpectedLoadingPortId()));
                detail.setExpectedLoadingPorts(portIds);
            }
            if(detail.getExpectedUnloadingPortId() != null){
                List<String> portIds = new LinkedList<>();
                portIds.add(String.valueOf(detail.getExpectedUnloadingPortId()));
                detail.setExpectedUnloadingPorts(portIds);
            }
        }
        return JsonResult.success(resultPage);
    }

    @Override
    public JsonResult<SpArrangeStatisticsVo> topStatistics() {
        SpArrangeStatisticsVo resultData = this.baseMapper.topStatistics();
        if(resultData == null){
            resultData = new SpArrangeStatisticsVo();
        }

        if(resultData.getTransportFinishWeight() != null){
            //百万因热需要转换成万吨
            BigDecimal transportFinishWeight = resultData.getTransportFinishWeight().multiply(new BigDecimal("0.475")).divide(new BigDecimal("22.5"),0,RoundingMode.HALF_UP);
            resultData.setTransportFinishWeight(transportFinishWeight);
        }

        //方案平均优化次数和实际优化次数
        List<CommonDataVo> disCountList = this.baseMapper.getDisCount();

        int allCount = 0;
        if(disCountList.size() > 0){
            int schemeCount = 0;
            for (CommonDataVo commonDataVo : disCountList) {
                allCount += Integer.parseInt(commonDataVo.getDataValue1());
                schemeCount ++;
            }
            BigDecimal allCountDec = new BigDecimal(String.valueOf(allCount));
            BigDecimal schemeCountDec = new BigDecimal(String.valueOf(schemeCount));
            resultData.setSchemeOperateCount(allCountDec);
            resultData.setSchemeOperateCountAvg(allCountDec.divide(schemeCountDec, 1, RoundingMode.CEILING));
        }
        //资源方占比
        List<SpArrangeSourceRateVo> sourceCountList = this.baseMapper.getSourceCount();
        if(sourceCountList.size() > 0 && allCount > 0){
            List<SpArrangeSourceRateVo> resourceSideRate = new LinkedList<>();
            for (SpArrangeSourceRateVo sourceRateVo : sourceCountList) {
                sourceRateVo.setSourceRate(sourceRateVo.getSourceCount().divide(
                        new BigDecimal(String.valueOf(allCount)), 2, RoundingMode.CEILING));
                resourceSideRate.add(sourceRateVo);
            }
            resultData.setResourceSideRate(resourceSideRate);
        }
        return JsonResult.success(resultData);
    }

    @Override
    public JsonResult statusUpdate(SpArrangeSchemeDetailStatusDto dto) {
        SpArrangeSchemeDetail schemeDetail = spArrangeSchemeDetailService.getById(dto.getSchemeDetailId());
        if(schemeDetail == null){
            return JsonResult.failed("方案信息异常！");
        }
        if(schemeDetail.getExecuteStatus().intValue() == dto.getExecuteStatus()){
            return JsonResult.failed("请勿重复操作！");
        }
//        if(0 == schemeDetail.getSchemeApprove()){
//            return JsonResult.failed("当前方案还有未完成的审批，请审批后再操作！");
//        }

        SpDispatchSchemeDetail dispatchSchemeDetail = spDispatchSchemeDetailService.getById(schemeDetail.getDispatchSchemeDetailId());
        if(dispatchSchemeDetail == null){
            return JsonResult.failed("调度方案信息异常！");
        }

        if(dto.getExecuteStatus().intValue() == 2){
            //下发可以直接下发，不走审批

            //如果是虚拟船则提示不能下发（虚拟船没有船可用）
            if(dispatchSchemeDetail.getImo().indexOf("virtualShip") > -1){
                return JsonResult.failed("船舶为虚拟船，该方案不能下发，请绑定船运合同，或调度优化，使用现有船只来进行运输！");
            }
            schemeDetail.setSchemeApprove(1);
            dispatchSchemeDetail.setAssigner(dto.getAssigner());

            //更新调度和船货执行状态
            schemeDetail.setExecuteStatus(2);
            dispatchSchemeDetail.setExecuteStatus(2);
            schemeDetail.setSchemeApprove(1);

            //下发需要实时生成航次号和货号
            Long sendCount = spDispatchSchemeDetailService.getBaseMapper().selectCount(
                    new LambdaQueryWrapper<SpDispatchSchemeDetail>()
                            .eq(SpDispatchSchemeDetail::getPlanYear,dispatchSchemeDetail.getPlanYear())
                            .eq(SpDispatchSchemeDetail::getSchemeId,dispatchSchemeDetail.getSchemeId())
                            .isNotNull(SpDispatchSchemeDetail::getAssigner)
            );
            //重新生成航次号
            Long vogeNo = (sendCount + 1);
            String vogeNoStr = vogeNo > 9 ? "" + vogeNo : "0"+vogeNo;
            if(StringUtils.isNotBlank(dispatchSchemeDetail.getSellerContractName()) && dispatchSchemeDetail.getSellerContractName().indexOf("返装销售") > -1){
                vogeNoStr = (vogeNoStr + "R");
            }
            dispatchSchemeDetail.setVoyageNo("CNOOC" + dispatchSchemeDetail.getPlanYear() + "" + vogeNoStr);
            schemeDetail.setVoyageNo("CNOOC" + dispatchSchemeDetail.getPlanYear() + "" + vogeNoStr);
            dispatchSchemeDetail.setSchemeNumber(vogeNo.intValue());
            schemeDetail.setSchemeNumber(vogeNo.intValue());

            Long sendGoodsCount = spDispatchSchemeDetailService.getBaseMapper().selectCount(
                    new LambdaQueryWrapper<SpDispatchSchemeDetail>()
                            .eq(SpDispatchSchemeDetail::getPlanYear,dispatchSchemeDetail.getPlanYear())
                            .eq(SpDispatchSchemeDetail::getSchemeId,dispatchSchemeDetail.getSchemeId())
                            .eq(SpDispatchSchemeDetail::getSeller,dispatchSchemeDetail.getSeller())
                            .isNotNull(SpDispatchSchemeDetail::getAssigner)
            );
            //重新生成货号
            Long goodsNo = (sendGoodsCount + 1);
            String goodsNoStr = goodsNo > 9 ? "" + goodsNo : "0"+goodsNo;
            if(StringUtils.isNotBlank(dispatchSchemeDetail.getSellerContractName()) && dispatchSchemeDetail.getSellerContractName().indexOf("返装销售") > -1){
                goodsNoStr = (goodsNoStr + "R");
            }
            dispatchSchemeDetail.setGoodsNum(dispatchSchemeDetail.getSeller() + "FOB" + goodsNoStr);
            schemeDetail.setGoodsNum(dispatchSchemeDetail.getSeller() + "FOB" + goodsNoStr);

            spArrangeSchemeDetailService.getBaseMapper().updateById(schemeDetail);
            //同步变更调度优化的方案状态
            spDispatchSchemeDetailService.getBaseMapper().updateById(dispatchSchemeDetail);

            resetVoyageNoAndGoodsNumberByYear(dispatchSchemeDetail.getPlanYear());

            //启动流程
            BpmStartVO bpmStartVO = new BpmStartVO();
            bpmStartVO.setProcessKey("");
            bpmStartVO.setAssigner(dto.getAssigner());
            bpmStartVO.setBusinessKey(schemeDetail.getId());
            processInstanceService.createProcessInstance(bpmStartVO);

            return JsonResult.success();
        }else{
            schemeDetail.setSchemeApprove(0);
            spArrangeSchemeDetailService.updateById(schemeDetail);
            ProcessInstanceParam param = new ProcessInstanceParam();
            param.setBpmKey(bpmInstanceService.ARRANGE_SCHEME_BPM_KEY);
            param.setBussinessId(dto.getSchemeDetailId());
            Map<String,Object> map = new HashMap<>();
            map.put("moduleName","船货排布");
            map.put("reason","");
            map.put("executeStatus",dto.getExecuteStatus());
            map.put("assigner",dto.getAssigner());
            param.setParams(map);
            bpmInstanceService.startProcessInstance(param);
            return JsonResult.success("方案发布已提交审批！");
        }
    }

    @Override
    public JsonResult arrangeSchemeApproveBack(Map<String,Object> param, Long schemeId, Integer approveStatus){
        SpArrangeSchemeDetail schemeDetail = spArrangeSchemeDetailService.getById(schemeId);
        if(schemeDetail == null){
            return JsonResult.failed("方案信息异常！");
        }

        if(2 == approveStatus){
            schemeDetail.setSchemeApprove(2);
            spArrangeSchemeDetailService.updateById(schemeDetail);
            return JsonResult.success();
        }

        SpDispatchSchemeDetail dispatchSchemeDetail = spDispatchSchemeDetailService.getById(schemeDetail.getDispatchSchemeDetailId());
        if(dispatchSchemeDetail == null){
            return JsonResult.failed("调度方案信息异常！");
        }

        Integer executeStatus = Integer.parseInt(param.get("executeStatus").toString());
        String assigner = param.get("assigner").toString();
        log.error("船货流程参数==================="+param);
        schemeDetail.setExecuteStatus(executeStatus);
        dispatchSchemeDetail.setExecuteStatus(executeStatus);
        dispatchSchemeDetail.setAssigner(assigner);
        if(executeStatus.intValue() == 2){
            //如果是虚拟船则提示不能下发（虚拟船没有船可用）
            if(dispatchSchemeDetail.getImo().indexOf("virtualShip") > -1){
                return JsonResult.failed("船舶为虚拟船，该方案不能下发，请绑定船运合同，或调度优化，使用现有船只来进行运输！");
            }
            //更新调度和船货执行状态
            schemeDetail.setExecuteStatus(2);
            dispatchSchemeDetail.setExecuteStatus(2);

            //下发需要实时生成航次号和货号
            Long sendCount = spDispatchSchemeDetailService.getBaseMapper().selectCount(
                    new LambdaQueryWrapper<SpDispatchSchemeDetail>()
                            .eq(SpDispatchSchemeDetail::getPlanYear,dispatchSchemeDetail.getPlanYear())
                            .eq(SpDispatchSchemeDetail::getSchemeId,dispatchSchemeDetail.getSchemeId())
                            .isNotNull(SpDispatchSchemeDetail::getAssigner)
            );
            //重新生成航次号
            Long vogeNo = (sendCount + 1);
            String vogeNoStr = vogeNo > 9 ? "" + vogeNo : "0"+vogeNo;
            if(StringUtils.isNotBlank(dispatchSchemeDetail.getSellerContractName()) && dispatchSchemeDetail.getSellerContractName().indexOf("返装销售") > -1){
                vogeNoStr = (vogeNoStr + "R");
            }
            dispatchSchemeDetail.setVoyageNo("CNOOC" + dispatchSchemeDetail.getPlanYear() + "" + vogeNoStr);
            schemeDetail.setVoyageNo("CNOOC" + dispatchSchemeDetail.getPlanYear() + "" + vogeNoStr);
            dispatchSchemeDetail.setSchemeNumber(vogeNo.intValue());
            schemeDetail.setSchemeNumber(vogeNo.intValue());

            Long sendGoodsCount = spDispatchSchemeDetailService.getBaseMapper().selectCount(
                    new LambdaQueryWrapper<SpDispatchSchemeDetail>()
                            .eq(SpDispatchSchemeDetail::getPlanYear,dispatchSchemeDetail.getPlanYear())
                            .eq(SpDispatchSchemeDetail::getSchemeId,dispatchSchemeDetail.getSchemeId())
                            .eq(SpDispatchSchemeDetail::getSeller,dispatchSchemeDetail.getSeller())
                            .isNotNull(SpDispatchSchemeDetail::getAssigner)
            );
            //重新生成货号
            Long goodsNo = (sendGoodsCount + 1);
            String goodsNoStr = goodsNo > 9 ? "" + goodsNo : "0"+goodsNo;
            if(StringUtils.isNotBlank(dispatchSchemeDetail.getSellerContractName()) && dispatchSchemeDetail.getSellerContractName().indexOf("返装销售") > -1){
                goodsNoStr = (goodsNoStr + "R");
            }
            dispatchSchemeDetail.setGoodsNum(dispatchSchemeDetail.getSeller() + "FOB" + goodsNoStr);
            schemeDetail.setGoodsNum(dispatchSchemeDetail.getSeller() + "FOB" + goodsNoStr);
        }else if(executeStatus.intValue() == 4){
            //终止
            SpDispatchSchemeExecute schemeExecute = spDispatchSchemeExecuteService.getBaseMapper().selectOne(
                    new LambdaQueryWrapper<SpDispatchSchemeExecute>()
                            .eq(SpDispatchSchemeExecute::getArrangeSchemeDetailId,schemeDetail.getId())
                            .last("limit 1")
            );
            if(schemeExecute != null && schemeExecute.getProcessInstanceId() != null){
                processInstanceService.cacenProcessInstance(schemeExecute.getProcessInstanceId());
            }

            schemeDetail.setExecuteStatus(4);
            dispatchSchemeDetail.setExecuteStatus(4);
        }else{
            return JsonResult.failed("未知的操作状态！");
        }
        schemeDetail.setSchemeApprove(1);
        spArrangeSchemeDetailService.getBaseMapper().updateById(schemeDetail);
        //同步变更调度优化的方案状态
        spDispatchSchemeDetailService.getBaseMapper().updateById(dispatchSchemeDetail);

        //更新航次号和货号
        resetVoyageNoAndGoodsNumberByYear(dispatchSchemeDetail.getPlanYear());

        if(executeStatus.intValue() == 2){
            //启动流程
            BpmStartVO bpmStartVO = new BpmStartVO();
            bpmStartVO.setProcessKey("");
            bpmStartVO.setAssigner(assigner);
            bpmStartVO.setBusinessKey(schemeDetail.getId());
            processInstanceService.createProcessInstance(bpmStartVO);
        }
        return JsonResult.success();
    }

    @Override
    public void resetVoyageNoAndGoodsNumberByYear(int year){
        List<SpArrangeSchemeDetail> detailList = spArrangeSchemeDetailService.getBaseMapper().selectList(
                new LambdaQueryWrapper<SpArrangeSchemeDetail>()
                .eq(SpArrangeSchemeDetail::getPlanYear,year)
        );
        detailList = detailList.stream().sorted(Comparator.comparing(SpArrangeSchemeDetail::getExpectedLoadingDate)).collect(Collectors.toList());
        //区分已下发和未下发
        List<SpArrangeSchemeDetail> sendList = detailList.stream().filter(detail -> detail.getExecuteStatus() > 1).collect(Collectors.toList());
        List<SpArrangeSchemeDetail> unSendList = detailList.stream().filter(detail -> detail.getExecuteStatus() == 1).collect(Collectors.toList());

        int sendSize = sendList.size();

        for (int i = 1 + sendSize; i <= unSendList.size() + sendSize; i++) {
            SpArrangeSchemeDetail detail = unSendList.get(i - sendSize - 1);
            //重新生成航次号
            String num = i > 9 ? ""+i : "0"+i;
            if(StringUtils.isNotBlank(detail.getSellerContractName()) && detail.getSellerContractName().indexOf("返装销售") > -1){
                num = (num + "R");
            }
            detail.setVoyageNo("CNOOC" + year + "" + num);
            detail.setSchemeNumber(i);
        }

        List<SpArrangeSchemeDetail> newDetailList = new LinkedList<>();
        newDetailList.addAll(sendList);
        newDetailList.addAll(unSendList);

        List<SpArrangeSchemeDetail> saveList = new LinkedList<>();

        //根据资源方分组重新生成各个资源方的货号
        Map<String, List<SpArrangeSchemeDetail>> detailMap = newDetailList.stream().collect(Collectors.groupingBy(SpArrangeSchemeDetail::getSeller));
        for (String seller : detailMap.keySet()) {
            List<SpArrangeSchemeDetail> mapDetails = detailMap.get(seller);
            mapDetails = mapDetails.stream().sorted(Comparator.comparing(SpArrangeSchemeDetail::getExpectedLoadingDate)).collect(Collectors.toList());

            //区分已下发和未下发
            List<SpArrangeSchemeDetail> sendGoodsList = mapDetails.stream().filter(detail -> detail.getExecuteStatus() > 1).collect(Collectors.toList());
            List<SpArrangeSchemeDetail> unSendGoodsList = mapDetails.stream().filter(detail -> detail.getExecuteStatus() == 1).collect(Collectors.toList());

            int sendGoodsSize = sendGoodsList.size();
            for (int i = 1 + sendGoodsSize; i <= unSendGoodsList.size() + sendGoodsSize; i++) {
                SpArrangeSchemeDetail mapDetail = unSendGoodsList.get(i - sendGoodsSize - 1);
                String num = i > 9 ? ""+i : "0"+i;
                if(StringUtils.isNotBlank(mapDetail.getSellerContractName()) && mapDetail.getSellerContractName().indexOf("返装销售") > -1){
                    num = (num + "R");
                }
                mapDetail.setGoodsNum(seller + "FOB" + num);
                saveList.add(mapDetail);
            }
            saveList.addAll(sendGoodsList);
        }
        for (SpArrangeSchemeDetail detail : saveList) {
            detail.setSchemeApprove(1);
            spArrangeSchemeDetailService.updateById(detail);
        }
    }

    @Override
    public JsonResult getDetail(Long detailId) {
        SpArrangeSchemeDetail schemeDetail = spArrangeSchemeDetailService.getById(detailId);
        if(schemeDetail == null){
            return JsonResult.failed("方案详情信息异常！");
        }

        //计算当前方案各项指标
        List<SpArrangeSchemeDetail> details = spArrangeSchemeDetailService.getBaseMapper().selectList(
                new LambdaQueryWrapper<SpArrangeSchemeDetail>()
                .eq(SpArrangeSchemeDetail::getSchemeId,schemeDetail.getSchemeId())
        );

        SpArrangeDataVo spArrangeDataVo = getSchemeUseRate(details);
        schemeDetail.setSpArrangeDataVo(spArrangeDataVo);
        return JsonResult.success(schemeDetail);
    }

    /**
     * 填充可用船只查询信息
     * @param spInformation
     * @param dto
     * @return
     */
    public JSONObject putSchemeDetail(SpInformation spInformation,ArrangeSchemeChartSimpleDto dto,ShipLoadDaysAndRouteVo shipLoadDaysAndRouteVo){
        Date expectLoadDate = dto.getExpectedLoadingDate();

        String boatName = spInformation.getNameOfVessel();
        JSONObject boatDetailJson = new JSONObject();
        List<SpArrangeSchemeDetail> details = new LinkedList<>();
        SpArrangeSchemeDetail schemeDetail = new SpArrangeSchemeDetail();
        //装载港
        schemeDetail.setExpectedLoadingPort(shipLoadDaysAndRouteVo.getStartPortName());
        schemeDetail.setExpectedLoadingPortId(shipLoadDaysAndRouteVo.getStartPortId());
        //卸载港
        schemeDetail.setExpectedUnloadingPort(shipLoadDaysAndRouteVo.getEndPortName());
        schemeDetail.setExpectedUnloadingPortId(shipLoadDaysAndRouteVo.getEndPortId());
        schemeDetail.setUnloadDistance(shipLoadDaysAndRouteVo.getTrackDistance());

        //装载港
        schemeDetail.setExpectedLoadingPort(shipLoadDaysAndRouteVo.getStartPortName());
        schemeDetail.setExpectedLoadingPortId(shipLoadDaysAndRouteVo.getStartPortId());
        //卸载港
        schemeDetail.setExpectedUnloadingPort(shipLoadDaysAndRouteVo.getEndPortName());
        schemeDetail.setExpectedUnloadingPortId(shipLoadDaysAndRouteVo.getEndPortId());
        schemeDetail.setPassStrait(shipLoadDaysAndRouteVo.getPassStrait());
        schemeDetail.setPassArea(shipLoadDaysAndRouteVo.getPassArea());
        schemeDetail.setUnloadDistance(shipLoadDaysAndRouteVo.getTrackDistance());

        int shipDays = 0;
        int chinaDays = 0;
        int europeDays = 0;
        int indiaDays = 0;

        if (shipLoadDaysAndRouteVo.getShipDay() != null) {
            shipDays = shipLoadDaysAndRouteVo.getShipDay();
        }
        if (shipLoadDaysAndRouteVo.getChinaDays() != null) {
            chinaDays = shipLoadDaysAndRouteVo.getChinaDays();
        }
        if (shipLoadDaysAndRouteVo.getEuropeDays() != null) {
            europeDays = shipLoadDaysAndRouteVo.getEuropeDays();
        }
        if (shipLoadDaysAndRouteVo.getIndiaDays() != null) {
            indiaDays = shipLoadDaysAndRouteVo.getIndiaDays();
        }
        schemeDetail.setCreateBy(SecurityUtils.getUsername());
        schemeDetail.setId(IDUtil.nextId());
        schemeDetail.setSchemeNumber(1);

        schemeDetail.setGoodsNum(schemeDetail.getSeller() + "-FOB-" + 1);

        schemeDetail.setExpectedLoadingDate(expectLoadDate);
        schemeDetail.setExpectedLoadingCompletionWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, 2));

        //预计卸载时间
        schemeDetail.setExpectedUnloadingWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, shipDays));
        //中国预计卸载时间
        schemeDetail.setChinaExpectedUnloadingWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, chinaDays));
        //印度预计卸载时间
        schemeDetail.setIndiaUnloadTime(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, indiaDays));
        //欧洲预计卸载时间
        schemeDetail.setEuropeUnloadTime(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, europeDays));
        //预计满载航程天数
        schemeDetail.setExpectedFullLoadVoyageDays(shipDays);
        //印度空载航程天数
        schemeDetail.setIndiaEmptyLoadDays(indiaDays);
        //欧洲空载航程天数
        schemeDetail.setEuropeEmptyLoadDays(europeDays);
        //预计卸载完成窗口
        schemeDetail.setExpectedUnloadingCompletionWindow(cn.hutool.core.date.DateUtil.offsetDay(schemeDetail.getExpectedUnloadingWindow(), 2));

        //预计空载返程时间
        schemeDetail.setExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~shipDays + 1));
        //中国预计空载返程窗口
        schemeDetail.setChinaExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~chinaDays + 1));
        //欧洲预计空载返程窗口
        schemeDetail.setEuropeExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~europeDays + 1));
        //印度预计空载返程窗口
        schemeDetail.setIndiaExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~indiaDays + 1));

        schemeDetail.setCreateDate(new Date());
        //TODO:巴拿马运河预定时间:装载时间提前30天 写死，后面从字典表中获取
        schemeDetail.setPanamaCanalReservationDate(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, -30));
        schemeDetail.setAvailableStatus(1);
        schemeDetail.setShipName(spInformation.getNameOfVessel());
        schemeDetail.setImo(spInformation.getImo());
        details.add(schemeDetail);

        boatDetailJson.put("shipName",boatName);
        boatDetailJson.put("details",details);
        return boatDetailJson;
    }

    @Override
    public JsonResult getAvailableArrangeSchemeChart(ArrangeSchemeChartSimpleDto dto) {
        List<SpInformation> allSpInformations = spInformationService.getAllSpInformationUsed(null);
        List<SpInformation> spInformations = new LinkedList<>();
        for (SpInformation allSpInformation : allSpInformations) {
            if(allSpInformation.getImo().indexOf("virtualShip") > -1){
                continue;
            }
            spInformations.add(allSpInformation);
        }
        //先获取船货里面的甘特图信息
        SchemeChartSimpleDto simpleDto = new SchemeChartSimpleDto();
        BeanCopyUtils.copy(dto,simpleDto);
        JSONObject resultData = new JSONObject();
        JSONArray boatsArray = new JSONArray();
        if(spInformations.size() == 0){
            return JsonResult.success(resultData);
        }
        for (SpInformation spInformation : spInformations) {
            String boatName = spInformation.getNameOfVessel();
            JSONObject boatDetailJson = new JSONObject();
            boatDetailJson.put("shipName",boatName);
            boatDetailJson.put("storageCapacity",spInformation.getStorageCapacity());
            boatDetailJson.put("storageCapacityMmbtu",spInformation.getStorageCapacityMmbtu());
            boatDetailJson.put("rentTimeSlot",JSONArray.parseArray(spInformation.getRentTimeSlot()));
            boatDetailJson.put("details",new LinkedList<>());
            boatsArray.add(boatDetailJson);
        }
        resultData.put("shipNames",boatsArray);
        resultData.put("planId",dto.getPlanId());
        resultData.put("goodsNames",new JSONArray());
        resultData.put("sellerNames",new JSONArray());
        List<JSONObject> newShipNamesArray = new LinkedList<>();
        JsonResult arrangeDate = this.getArrangeSchemeChart(simpleDto);

        ShipLoadDaysAndRouteVo shipLoadDaysAndRouteVo = null;
        if(dto.getExpectedLoadingDate() != null && dto.getLoadingSort() != null && dto.getLoadingSort().size() > 0) {
            shipLoadDaysAndRouteVo = spPortRouteDistanceService.getCommonLoadDay(dto.getLoadingSort()
                    , dto.getUnloadSort(), dto.getSpeed(), dto.getPassStrait(), dto.getPassArea());
            if (shipLoadDaysAndRouteVo == null) {
                throw new ServiceException("当前航线暂无可匹配的路线信息", BaseResultCode.GENERAL_ERROR.getCode());
            }
        }

        if(arrangeDate.getResultCode().intValue() != 0 || arrangeDate.getResultObject() == null
                || ((JSONObject)arrangeDate.getResultObject()).get("shipNames") == null){
            if(dto.getExpectedLoadingDate() != null && dto.getLoadingSort() != null && dto.getLoadingSort().size() > 0){
                //装载港和装载时间都传了的情况下才需要进行模拟排货
                for (SpInformation spInformation : spInformations) {
                    newShipNamesArray.add(putSchemeDetail(spInformation,dto,shipLoadDaysAndRouteVo));
                }
                resultData.put("shipNames",newShipNamesArray);
            }
            return JsonResult.success(resultData);
        }

        JSONObject resultObject = ((JSONObject)arrangeDate.getResultObject());
        JSONArray shipNames = resultObject.getJSONArray("shipNames");
        if(dto.getExpectedLoadingDate() != null && dto.getLoadingSort() != null && dto.getLoadingSort().size() > 0){
            //传入了装载时间，说明要模拟排货
            if(shipNames.size() == 0){
                for (SpInformation spInformation : spInformations) {
                    newShipNamesArray.add(putSchemeDetail(spInformation,dto,shipLoadDaysAndRouteVo));
                }
                resultData.put("shipNames",newShipNamesArray);
                return JsonResult.success(resultData);
            }
            Date expectLoadDate = dto.getExpectedLoadingDate();
            for (Object shipName : shipNames) {
                JSONObject shipJson = JSONObject.parseObject(shipName.toString());
                String name = shipJson.getString("shipName");
                if(name.indexOf("虚拟船") > -1){
                    continue;
                }
                SpArrangeSchemeDetail schemeDetail = new SpArrangeSchemeDetail();
                //装载港
                schemeDetail.setExpectedLoadingPort(shipLoadDaysAndRouteVo.getStartPortName());
                schemeDetail.setExpectedLoadingPortId(shipLoadDaysAndRouteVo.getStartPortId());
                //卸载港
                schemeDetail.setExpectedUnloadingPort(shipLoadDaysAndRouteVo.getEndPortName());
                schemeDetail.setExpectedUnloadingPortId(shipLoadDaysAndRouteVo.getEndPortId());
                schemeDetail.setUnloadDistance(shipLoadDaysAndRouteVo.getTrackDistance());

                //装载港
                schemeDetail.setExpectedLoadingPort(shipLoadDaysAndRouteVo.getStartPortName());
                schemeDetail.setExpectedLoadingPortId(shipLoadDaysAndRouteVo.getStartPortId());
                //卸载港
                schemeDetail.setExpectedUnloadingPort(shipLoadDaysAndRouteVo.getEndPortName());
                schemeDetail.setExpectedUnloadingPortId(shipLoadDaysAndRouteVo.getEndPortId());
                schemeDetail.setPassStrait(shipLoadDaysAndRouteVo.getPassStrait());
                schemeDetail.setPassArea(shipLoadDaysAndRouteVo.getPassArea());
                schemeDetail.setUnloadDistance(shipLoadDaysAndRouteVo.getTrackDistance());

                int shipDays = 0;
                int chinaDays = 0;
                int europeDays = 0;
                int indiaDays = 0;

                if (shipLoadDaysAndRouteVo.getShipDay() != null) {
                    shipDays = shipLoadDaysAndRouteVo.getShipDay();
                }
                if (shipLoadDaysAndRouteVo.getChinaDays() != null) {
                    chinaDays = shipLoadDaysAndRouteVo.getChinaDays();
                }
                if (shipLoadDaysAndRouteVo.getEuropeDays() != null) {
                    europeDays = shipLoadDaysAndRouteVo.getEuropeDays();
                }
                if (shipLoadDaysAndRouteVo.getIndiaDays() != null) {
                    indiaDays = shipLoadDaysAndRouteVo.getIndiaDays();
                }
                schemeDetail.setCreateBy(SecurityUtils.getUsername());
                schemeDetail.setId(IDUtil.nextId());
                schemeDetail.setSchemeNumber(1);

                schemeDetail.setGoodsNum(schemeDetail.getSeller() + "-FOB-" + 1);

                schemeDetail.setExpectedLoadingDate(expectLoadDate);
                schemeDetail.setExpectedLoadingCompletionWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, 2));

                //预计卸载时间
                schemeDetail.setExpectedUnloadingWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, shipDays));
                //中国预计卸载时间
                schemeDetail.setChinaExpectedUnloadingWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, chinaDays));
                //印度预计卸载时间
                schemeDetail.setIndiaUnloadTime(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, indiaDays));
                //欧洲预计卸载时间
                schemeDetail.setEuropeUnloadTime(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, europeDays));
                //预计满载航程天数
                schemeDetail.setExpectedFullLoadVoyageDays(shipDays);
                //印度空载航程天数
                schemeDetail.setIndiaEmptyLoadDays(indiaDays);
                //欧洲空载航程天数
                schemeDetail.setEuropeEmptyLoadDays(europeDays);
                //预计卸载完成窗口
                schemeDetail.setExpectedUnloadingCompletionWindow(cn.hutool.core.date.DateUtil.offsetDay(schemeDetail.getExpectedUnloadingWindow(), 2));

                //预计空载返程时间
                schemeDetail.setExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~shipDays + 1));
                //中国预计空载返程窗口
                schemeDetail.setChinaExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~chinaDays + 1));
                //欧洲预计空载返程窗口
                schemeDetail.setEuropeExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~europeDays + 1));
                //印度预计空载返程窗口
                schemeDetail.setIndiaExpectedEmptyReturnWindow(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, ~indiaDays + 1));

                schemeDetail.setCreateDate(new Date());
                //TODO:巴拿马运河预定时间:装载时间提前30天 写死，后面从字典表中获取
                schemeDetail.setPanamaCanalReservationDate(cn.hutool.core.date.DateUtil.offsetDay(expectLoadDate, -30));


                JSONArray details = shipJson.getJSONArray("details");
                SpArrangeSchemeDetail detail = JSONObject.parseObject(details.get(0).toString(),SpArrangeSchemeDetail.class);
                //每一艘船都新增一条模拟的方案数据，并进行船只可用状态校验
                schemeDetail.setShipName(name);
                schemeDetail.setImo(detail.getImo());
                schemeDetail.setSchemeId(detail.getSchemeId());
                BigDecimal mut = BigDecimal.ZERO;
                if(detail.getLoadingCapacity() != null){
                    mut = detail.getLoadingCapacity().multiply(BigDecimal.valueOf(0.985))
                            .subtract(BigDecimal.valueOf(500)).multiply(BigDecimal.valueOf(22.5))
                            .setScale(0, BigDecimal.ROUND_DOWN);
                }

                if (dto.getGoodsQuantity() != null) {
                    schemeDetail.setSingleGoodsQuantity(dto.getGoodsQuantity());
                } else {
                    schemeDetail.setSingleGoodsQuantity(mut);
                }
                schemeDetail.setOilConsumption(detail.getOilConsumption());

                Long count = spArrangeSchemeDetailService.getBaseMapper().selectCount(
                        new LambdaQueryWrapper<SpArrangeSchemeDetail>()
                                .eq(SpArrangeSchemeDetail::getSchemeId,schemeDetail.getSchemeId())
                                .eq(SpArrangeSchemeDetail::getImo,schemeDetail.getImo())
                                .and(qw -> {
                                    qw.between(SpArrangeSchemeDetail::getExpectedEmptyReturnWindow,schemeDetail.getExpectedEmptyReturnWindow(),schemeDetail.getExpectedUnloadingWindow());
                                    qw.or().between(SpArrangeSchemeDetail::getExpectedUnloadingWindow,schemeDetail.getExpectedEmptyReturnWindow(),schemeDetail.getExpectedUnloadingWindow());
                                })
                );


                //判断是否在出租期内
                List<ConShipping> rentShip = conShippingService.getBaseMapper().selectList(
                        new LambdaQueryWrapper<ConShipping>()
                                .eq(ConShipping::getImo,schemeDetail.getImo())
                                .ne(ConShipping::getStatus,4)
                                .eq(ConShipping::getContractType,3)
                );
                Boolean inRent = false;
                for (ConShipping conShipping : rentShip) {
                    //空载时间和装载时间在租期内，则表示已租出去，不能使用
                    if ((schemeDetail.getExpectedEmptyReturnWindow().before(conShipping.getRedeliveryDateEnd())
                            && schemeDetail.getExpectedEmptyReturnWindow().after(conShipping.getDeliveryDateBegin()))
                            || (schemeDetail.getExpectedUnloadingWindow().after(conShipping.getDeliveryDateBegin())
                            && schemeDetail.getExpectedUnloadingWindow().before(conShipping.getRedeliveryDateEnd()))) {
                        inRent = true;
                    }
                }

                //是否处于OFF-HIRE区间
                rentShip = conShippingService.getBaseMapper().selectList(
                        new LambdaQueryWrapper<ConShipping>()
                                .eq(ConShipping::getImo,schemeDetail.getImo())
                                .eq(ConShipping::getStatus,5)
                );
                for (ConShipping conShipping : rentShip) {
                    if(StringUtils.isBlank(conShipping.getHireTimeValue())){
                        continue;
                    }
                    ObjectMapper objectMapper = new ObjectMapper();
                    try {
                        List<HireTimeDetailsBO> hireTimeDetailsBOS = objectMapper.readValue(conShipping.getHireTimeValue()
                                , new TypeReference<List<HireTimeDetailsBO>>() {});
                        if(hireTimeDetailsBOS.size() == 0){
                            continue;
                        }
                        for (HireTimeDetailsBO hireTimeDetailsBO : hireTimeDetailsBOS) {
                            Boolean inStatus = (hireTimeDetailsBO.getStart().after(schemeDetail.getExpectedEmptyReturnWindow())
                                    && hireTimeDetailsBO.getStart().before(schemeDetail.getExpectedUnloadingWindow()))
                                    || (hireTimeDetailsBO.getEnd().after(schemeDetail.getExpectedEmptyReturnWindow())
                                    && hireTimeDetailsBO.getEnd().before(schemeDetail.getExpectedUnloadingWindow()))
                                    || (hireTimeDetailsBO.getEnd().after(schemeDetail.getExpectedUnloadingWindow())
                                    && hireTimeDetailsBO.getStart().before(schemeDetail.getExpectedEmptyReturnWindow()));
                            if(inStatus){
                                inRent = true;
                            }
                        }
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                }
                //是否在租入时间内
                rentShip = conShippingService.getBaseMapper().selectList(
                        new LambdaQueryWrapper<ConShipping>()
                                .eq(ConShipping::getImo,schemeDetail.getImo())
                                .ne(ConShipping::getStatus,4)
                                .in(ConShipping::getContractType,1,2)
                );
                for (ConShipping conShipping : rentShip) {
                    if (schemeDetail.getExpectedLoadingDate().after(conShipping.getRedeliveryDateEnd())
                            || schemeDetail.getExpectedLoadingDate().before(conShipping.getDeliveryDateBegin())) {
                        inRent = true;
                    }
                }

                if(count > 0 || name.indexOf("虚拟船") > 0 || inRent){
                    schemeDetail.setAvailableStatus(3);
                }else{
                    schemeDetail.setAvailableStatus(1);
                }

                details.add(schemeDetail);
                shipJson.put("details",details);
                newShipNamesArray.add(shipJson);
            }
        }else{
            //如果装载时间为空，需要判断卸载时间和卸载量如果不为空则根据这两个值来筛选数据，如果都为空，则直接返回原始数据，否则进行筛选
            if(dto.getUnloadGoodsQuantity() != null || dto.getExpectedUnloadingWindow() != null){
                Date firstTime = null;
                for (Object shipName : shipNames) {
                    JSONObject shipJson = JSONObject.parseObject(shipName.toString());
                    String name = shipJson.getString("shipName");
                    if(name.indexOf("虚拟船") > -1){
                        continue;
                    }
                    JSONArray details = shipJson.getJSONArray("details");
                    if(details.size() == 0){
                        newShipNamesArray.add(shipJson);
                        continue;
                    }
                    JSONArray newJsonArray = new JSONArray();
                    for (Object detail : details) {
                        SpArrangeSchemeDetail schemeDetail = JSONObject.parseObject(detail.toString(),SpArrangeSchemeDetail.class);
                        if(dto.getUnloadGoodsQuantity() != null){
                            //装载量必须大于等于输入的卸载量
                            BigDecimal goodsQuantityNew = dto.getUnloadGoodsQuantity();
                            if (dto.getQuantityUnit() != null) {
                                if(dto.getQuantityUnit().equals("2")){
                                    //单位为立方米时转为百万英热
                                    goodsQuantityNew = dto.getUnloadGoodsQuantity().multiply(BigDecimal.valueOf(0.985))
                                            .subtract(BigDecimal.valueOf(500)).multiply(BigDecimal.valueOf(22.5))
                                            .setScale(0, BigDecimal.ROUND_DOWN);
                                }else if(dto.getQuantityUnit().equals("3")){
                                    // //单位为MT（万吨）转为百万英热 1mt = 47.3684mmbtu
                                    goodsQuantityNew = dto.getUnloadGoodsQuantity().multiply(BigDecimal.valueOf(47.3684))
                                            .setScale(0, BigDecimal.ROUND_DOWN);
                                }
                            }
                            if(schemeDetail.getSingleGoodsQuantity().compareTo(goodsQuantityNew) < 0){
                                continue;
                            }
                        }
                        if(dto.getExpectedUnloadingWindow() != null){
                            //输入了卸载时间，则根据卸载时间往前推七天来校验货的卸载时间是否在这个区间内
                            Date inputUnloadDate = dto.getExpectedUnloadingWindow();
                            Date unloadDate = cn.hutool.core.date.DateUtil.offsetDay(dto.getExpectedUnloadingWindow(),-7);
                            if(schemeDetail.getExpectedUnloadingWindow().before(unloadDate)
                                    || schemeDetail.getExpectedUnloadingWindow().after(inputUnloadDate)){
                                continue;
                            }
                        }
                        if(firstTime == null){
                            firstTime = schemeDetail.getExpectedLoadingDate();
                        }else{
                            if(firstTime.after(schemeDetail.getExpectedLoadingDate())){
                                firstTime = schemeDetail.getExpectedLoadingDate();
                            }
                        }
                        newJsonArray.add(schemeDetail);
                    }
                    shipJson.put("details",newJsonArray);
                    newShipNamesArray.add(shipJson);
                }
                resultObject.put("shipNames",newShipNamesArray);
                arrangeDate.setResultObject(resultObject);
            }else{
                for (Object shipName : shipNames) {
                    JSONObject shipJson = JSONObject.parseObject(shipName.toString());
                    String name = shipJson.getString("shipName");
                    if(name.indexOf("虚拟船") > -1){
                        continue;
                    }
                    List<SpArrangeSchemeDetail> detailList = JSONObject.parseArray(shipJson.getString("details"),SpArrangeSchemeDetail.class);
                    shipJson.put("details",detailList);
                    newShipNamesArray.add(shipJson);
                }
                resultObject.put("shipNames",newShipNamesArray);
                arrangeDate.setResultObject(resultObject);
            }
        }

        for (SpInformation spInformation : spInformations) {
            Boolean isAdd = true;
            for (JSONObject jsonObject : newShipNamesArray) {
                if(spInformation.getNameOfVessel().equals(jsonObject.getString("shipName"))){
                    isAdd = false;
                    break;
                }
            }
            if(isAdd){
                if(dto.getExpectedLoadingDate() != null && dto.getLoadingSort() != null && dto.getLoadingSort().size() > 0){
                    newShipNamesArray.add(putSchemeDetail(spInformation,dto,shipLoadDaysAndRouteVo));
                }
            }
        }
        resultObject.put("shipNames",newShipNamesArray);
        arrangeDate.setResultObject(resultObject);

        return arrangeDate;
    }

    /**
     * 根据方案详情获取方案使用情况数值
     * @param details
     * @return
     */
    public SpArrangeDataVo getSchemeUseRate(List<SpArrangeSchemeDetail> details){
        SpArrangeDataVo spArrangeDataVo = new SpArrangeDataVo();
        //航行天数
        long travelDays = 0L;
        //总航行天数
        long allTravelDays = 0L;
        //运费
        BigDecimal freight = BigDecimal.ZERO;
        for (SpArrangeSchemeDetail detail : details) {
            if(detail.getExpectedLoadingDate() != null && detail.getExpectedUnloadingWindow() != null){
                travelDays = (travelDays + DateUtil.getDateDays(detail.getExpectedLoadingDate(),detail.getExpectedUnloadingWindow(),1));
            }
            if(detail.getExpectedLoadingDate() != null && detail.getExpectedEmptyReturnWindow() != null){
                allTravelDays = (allTravelDays + DateUtil.getDateDays(detail.getExpectedLoadingDate(),detail.getExpectedEmptyReturnWindow(),1));
            }
            if(detail.getFreight() != null){
                freight = freight.add(detail.getFreight());
            }
        }
        spArrangeDataVo.setFreight(freight);
        spArrangeDataVo.setTravelDays(travelDays);

        int year = details.get(0).getPlanYear();
        //获取当前年度所有船只总的租期（天），来计算整个方案船队的利用率
        List<ConShipping> shippings = conShippingService.getBaseMapper().selectList(
                new LambdaQueryWrapper<ConShipping>()
                .last("where YEAR(starting_date_of_lease_begin) <= "+year+" AND YEAR(starting_date_of_lease_end) >= "+year)
        );
        if(shippings.size() == 0){
            return spArrangeDataVo;
        }
        Date startDate = com.ship.common.core.utils.DateUtil.String2Date(year+"-01-01 00:00:00", "yyyy-MM-dd");
        Date endDate = com.ship.common.core.utils.DateUtil.String2Date(year+"-12-31 23:59:59", "yyyy-MM-dd");
        long leaseDays = 0L;
        for (ConShipping shipping : shippings) {
            Date leaseStart = shipping.getDeliveryDateBegin().before(startDate) ? startDate : shipping.getDeliveryDateBegin();
            Date leaseEnd = shipping.getStartingDateOfLeaseEnd().after(endDate) ? endDate : shipping.getStartingDateOfLeaseEnd();
            leaseDays = (leaseDays + DateUtil.getDateDays(leaseStart,leaseEnd,1));
        }
        BigDecimal useRate = new BigDecimal(String.valueOf(allTravelDays))
                .divide(new BigDecimal(String.valueOf(leaseDays)),2,BigDecimal.ROUND_UP);
        spArrangeDataVo.setShipUseRate(useRate.multiply(new BigDecimal("100")));
        return spArrangeDataVo;
    }

}




